package com.shangxia.lucky.lucky.job;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.shangxia.lucky.common.util.file.ReadTXTUtil;
import com.shangxia.lucky.lucky.ConfigText;
import com.shangxia.lucky.lucky.common.FactoryHead;
import com.shangxia.lucky.lucky.entity.Model;
import com.shangxia.lucky.lucky.entity.SheetType;
import com.shangxia.lucky.lucky.entity.TypeList;
import com.shangxia.lucky.lucky.writeHandler.MyOverExcelWriteHandler;
import com.shangxia.lucky.lucky.writeHandler.MyTrueWriteHandler;
import com.shangxia.lucky.lucky.writeHandler.MyTypeWriteHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.*;

/**
 * @ProjectName: monomer
 * @ClassName: Match2Job
 * @Time: 2021/8/13
 * @Author: zhouy
 * @Description:
 * @Experience:
 */

@Slf4j
@Service
public class Match2Job extends BaseJob {

    private static List<List<Integer>> readCodesLists = new ArrayList<>();
    private static List<List<Integer>> matchLists = new ArrayList<>();
    private static List<List<Integer>> matchListsAll = new ArrayList<>();
    //统计最后一层
    private static List<Integer> LAST_LIST;
    private static List<WriteSheet> overSheets = new ArrayList<>();
    private static List<Integer> ORDER_LIST = new ArrayList<>();
    private static Map<SheetType, WriteSheet> writeSheetMap = new HashMap<>();
    private static Map<SheetType, ExcelWriter> excelWriterMap = new HashMap<>();
    private static List<TypeList> LONGEST_TYPE_LIST = new ArrayList<>();
    private static List<List<String>> LONGEST_TYPE_RESULT = new ArrayList<>();
    private static List<List<String>> LAST_GROUP_TYPE_RESULT = new ArrayList<>();


    @Autowired
    private ConfigText configText;


    public void doJob() throws IOException {
        //读取号码
        readCode();
        //定义输出文件
        if (!configText.getBananaMatchLimitOpen()) {
            createWriteAge();
            createWriteOver();
        } else {
            createWriteBatch();
        }
        log.info("计算单行总列数={}", matchLists.size() * configText.getBananaExportColumn());
        int limit = configText.getBananaMatchLimit();
        int group = 1;
        List<List<Integer>> rowLastList = null;
        if (configText.getBananaMatchLimitOpen()) {
            for (int i = 0; i < matchListsAll.size(); i += limit) {
                log.info("开始计算第 {} 组", group);
                matchLists.clear();
                matchLists.addAll(matchListsAll.subList(i, Math.min(i + limit, matchListsAll.size())));
                rowLastList = execute(group++);
                //TODO 每批保留 N 组
                putLongestResult(i);
            }
        } else {
            matchLists = matchListsAll;
            rowLastList = execute(group++);
            putLongestResult(0);
        }

        //关闭流
        if (!configText.getBananaMatchLimitOpen()) {
            //输出 ageCount 最后一行
            exportAgeCountLastRow();
            excelWriterMap.get(SheetType.AGE).finish();
            if (configText.bananaOverExportOpen) {
                //翻层最后一层
                exportLastRow(rowLastList);
                excelWriterMap.get(SheetType.OVER).finish();
            }
        } else {
            excelWriterMap.get(SheetType.BATCH).write(LAST_GROUP_TYPE_RESULT, writeSheetMap.get(SheetType.BATCH));
            excelWriterMap.get(SheetType.BATCH).finish();
        }

    }

    private void putLongestResult(int type) {

        LONGEST_TYPE_LIST.stream()
                .sorted(Comparator.comparingInt(TypeList::getGroup))
                .sorted(Comparator.comparingInt(TypeList::getAge))
                .sorted(Comparator.comparingInt(TypeList::getColumn))
                .sorted(Comparator.comparingInt(TypeList::getType)).forEach(e -> {
            List<String> list = new ArrayList<>();
            list.add("" + e.getGroup());
            list.add("" + e.getAge());
            list.add("" + e.getColumn());
            list.add((type + e.getType()) + "");
            StringBuilder str = new StringBuilder();
            for (Integer integer : e.getList()) {
                str.append(integer).append(" ");
            }
            list.add(str.toString());
            //分组保留年龄最大
            //LONGEST_TYPE_RESULT.add(list);
            //分组保留最近数据
            LAST_GROUP_TYPE_RESULT.add(list);
        });
        LONGEST_TYPE_LIST.clear();
    }

    private void sortByAge() {
        Map<String, List<TypeList>> map = LONGEST_TYPE_LIST.stream().collect(groupingBy(e -> e.getColumn() + "&" + e.getType()));
        LONGEST_TYPE_LIST = map.values().stream().map(e -> {
            e = e.stream().sorted(Comparator.comparingInt(TypeList::getAge)).collect(Collectors.toList());
            return e.get(e.size() - 1);
        }).sorted(Comparator.comparingInt(TypeList::getAge).reversed())
                .limit(Math.min(map.size(), configText.getBananaMatchSaveSize()))
                .collect(Collectors.toList());
    }

    /**
     * 核心逻辑 计算结果数据
     *
     * @param group
     * @return
     */
    public List<List<Integer>> execute(Integer group) {
        //上一行记录
        List<List<Integer>> rowLastList = new ArrayList<>();
        Map<String, Model> typeRowLastMap = new LinkedHashMap<>();

        //计算过程
        for (int row = 0; row < readCodesLists.size(); row++) {
            //当前行记录
            List<List<Integer>> rowList = new ArrayList<>();
            Map<String, Model> typeRowMap = new LinkedHashMap<>();
            //翻表开始 begin
            log.info("正在计算第 {} 行", 1 + row);
            for (int column = 0; column <= Math.min(row, configText.getBananaOverMaxColumn() - 1); column++) {

                List<Integer> columnList;
                if (row == 0) {
                    columnList = new ArrayList<>(ORDER_LIST);
                    columnList.removeAll(readCodesLists.get(0));
                    columnList.addAll(readCodesLists.get(0));
                } else if (column == 0) {
                    ArrayList<Integer> lastRowColumnList = new ArrayList<>(rowLastList.get(0));
                    lastRowColumnList.removeAll(readCodesLists.get(row));
                    lastRowColumnList.addAll(readCodesLists.get(row));
                    columnList = lastRowColumnList;
                } else {
                    List<Integer> lastColumnList = new ArrayList(rowList.get(column - 1));
                    List<Integer> realCodes = new ArrayList();
                    for (int i = 0; i < configText.getBananaSonCodeLength(); i++) {
                        realCodes.add(rowLastList.get(column - 1)
                                .indexOf(lastColumnList.get(configText.getBananaCodeLength() - configText.getBananaSonCodeLength() + i)) + 1);
                    }
                    if (rowLastList.size() < column + 2) {
                        columnList = new ArrayList(ORDER_LIST);
                    } else {
                        columnList = new ArrayList(rowLastList.get(column));
                    }
                    columnList.removeAll(realCodes);
                    columnList.addAll(realCodes);
                }

                rowList.add(columnList);
                //翻表开始 end
                //计算匹配情况
                for (int type = 0; type < matchLists.size(); type++) {
                    Set<Integer> set = new HashSet<>(matchLists.get(type));
                    set.addAll(columnList.subList(configText.getBananaCodeLength() - configText.getBananaSonCodeLength(), columnList.size()));
                    //匹配个数
                    int matchCount = configText.getBananaSonCodeLength() + matchLists.get(type).size() - set.size();

                    Model model = new Model();
                    model.setRow(row);
                    model.setColumn(column);
                    model.setType(type);
                    model.setMatchCount(matchCount);
                    if (row == column) {
                        model.setAge(1);
                        model.setMatchCountAll(matchCount);
                    } else {
                        Model lastModel = typeRowLastMap.get(column + "&" + type);
                        if (lastModel.getPrized()) {
                            model.setAge(1);
                        } else {
                            model.setAge(lastModel.getAge() + 1);
                        }
                        model.setMatchCountAll(lastModel.getMatchCountAll() + matchCount);
                    }
                    typeRowMap.put(column + "&" + type, model);
                    if (0 != model.getMatchCountAll()) {
                        Double aveMatchCount = 1.0 * model.getMatchCountAll() / (1 + row - column);
                        model.setAveMatchCount(String.format("%.2f", aveMatchCount));
                    } else {
                        model.setAveMatchCount("0");
                    }
                    model.setPrized(matchCount == Math.min(matchLists.get(type).size(), configText.getBananaSonCodeLength()));
                    //分组保留年龄最大
//                    LONGEST_TYPE_LIST.add(new TypeList(group, column + 1, type + 1, model.getAge(), matchLists.get(type)));
                }
            }
            //本层分析计算结束
            List<Model> typeList = new ArrayList<>(typeRowMap.values());
            List<Model> resultList = typeList.stream().sorted(Comparator.comparingInt(Model::getType))
                    .sorted(Comparator.comparingInt(Model::getColumn))
                    .sorted(Comparator.comparingInt(Model::getAge).reversed())
                    .limit(Math.min(typeList.size(), configText.getBananaExportColumn())).collect(toList());
            if (!configText.getBananaMatchLimitOpen()) {
                lastRowData(resultList);
                //输出翻层
                if (configText.bananaOverExportOpen) {
                    exoprtOverResult(rowLastList, row, rowList);
                }
                //计算本层输出结果
                exportRowResult(resultList);
            } else {
                if (readCodesLists.size() - row <= configText.getBananaMatchSaveSize()) {
                    for (Model model : resultList) {
                        // 分组保留最近数据
                        LONGEST_TYPE_LIST.add(new TypeList(group, model.getColumn() + 1, model.getType() + 1, model.getAge(), matchLists.get(model.getType())));
                    }
                }
            }
            rowLastList = rowList;
            typeRowLastMap = typeRowMap;
        }
        return rowLastList;
    }

    private void exportRowResult(List<Model> resultList) {
        Map<String, List<String>> map = countResult(resultList);
        excelWriterMap.get(SheetType.AGE).write(getBigList(map.get("age")), writeSheetMap.get(SheetType.AGE));
        excelWriterMap.get(SheetType.AGE).write(getBigList(map.get("type")), writeSheetMap.get(SheetType.TYPE));
    }

    private void createWriteBatch() {
        String batchFile = configText.getBananaAbsoluteResultPath() + DateFormatUtils.format(new Date(), "yyyy-MM-dd_H-mm-ss_分批") + ".xlsx";
        ExcelWriter excelWriter = EasyExcel.write(batchFile).build();
        WriteSheet ageSheet = EasyExcel.writerSheet("分批")
                .head(FactoryHead.groupHead()).build();
        writeSheetMap.put(SheetType.BATCH, ageSheet);
        excelWriterMap.put(SheetType.BATCH, excelWriter);
    }

    private void createWriteAge() {
        String file = configText.getBananaAbsoluteResultPath() + DateFormatUtils.format(new Date(), "yyyy-MM-dd_H-mm-ss") + ".xlsx";
        ExcelWriter excelWriter = EasyExcel.write(file).build();
        WriteSheet ageSheet = EasyExcel.writerSheet("统计")
                .registerWriteHandler(new MyTrueWriteHandler()).head(FactoryHead.ageHead(configText.getBananaExportColumn())).build();
        WriteSheet typeSheet = EasyExcel.writerSheet("种类")
                .registerWriteHandler(new MyTypeWriteHandler()).head(FactoryHead.typeHead(configText.getBananaExportColumn())).build();
        writeSheetMap.put(SheetType.AGE, ageSheet);
        writeSheetMap.put(SheetType.TYPE, typeSheet);
        excelWriterMap.put(SheetType.AGE, excelWriter);

    }

    private void createWriteOver() {
        for (int i = 0; i < configText.getBananaOverMaxColumn(); i++) {
            overSheets.add(EasyExcel.writerSheet(i, "第" + (i + 1) + "层")
                    .registerWriteHandler(new MyOverExcelWriteHandler())
                    .head(FactoryHead.head(i, configText.getBananaCodeLength())).build());
        }
        String overFile = configText.getBananaAbsoluteResultPath() + DateFormatUtils.format(new Date(), "yyyy-MM-dd_H-mm-ss_翻层表") + ".xlsx";
        ExcelWriter excelOverWriter = EasyExcel.write(overFile).build();
        excelWriterMap.put(SheetType.OVER, excelOverWriter);
    }

    private void exportAgeCountLastRow() {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < LAST_LIST.size(); i++) {
            list.add(0 == LAST_LIST.get(i) ? " " : "" + LAST_LIST.get(i));
        }
        excelWriterMap.get(SheetType.AGE).write(getBigList(list), writeSheetMap.get(SheetType.AGE));
    }

    private void exoprtOverResult(List<List<Integer>> rowLastList, int row, List<List<Integer>> rowList) {
        if (CollectionUtils.isEmpty(rowLastList)) {
            return;
        }
        for (int i = 0; i < configText.getBananaOverMaxColumn(); i++) {
            List<List<String>> exportList = new ArrayList<>();
            List<String> list = new ArrayList<>();
            list.add(row + "");
            try {
                rowLastList.get(i).stream().forEach(e -> {
                    list.add("" + e);
                });
                exportList.add(list);
                for (Integer num : rowList.get(i).subList(configText.getBananaCodeLength() - configText.getBananaSonCodeLength(), rowList.get(i).size())) {
                    MyOverExcelWriteHandler.RED_LAST_LIST.add("" + num);
                }
            } catch (IndexOutOfBoundsException e) {
                exportList.add(new ArrayList<>());
            }
            excelWriterMap.get(SheetType.OVER).write(exportList, overSheets.get(i));
            MyOverExcelWriteHandler.RED_LAST_LIST.clear();
        }
    }

    /**
     * 输出翻层最后一层
     *
     * @param rowLastList
     */
    private void exportLastRow(List<List<Integer>> rowLastList) {
        for (int i = 0; i < configText.getBananaOverMaxColumn(); i++) {
            List<List<String>> exportList = new ArrayList<>();
            List<String> list0 = new ArrayList<>();
            list0.add(readCodesLists.size() + "");
            try {
                rowLastList.get(i).stream().forEach(e -> {
                    list0.add("" + e);
                });
                exportList.add(list0);
            } catch (IndexOutOfBoundsException e) {
                exportList.add(new ArrayList<>());
            }
            excelWriterMap.get(SheetType.OVER).write(exportList, overSheets.get(i));
        }
    }

    private List<List<String>> getBigList(List<String> age) {
        List<List<String>> list = new ArrayList<>();
        list.add(age);
        return list;
    }

    public void readCode() throws IOException {
        //读取号码
        readCodesLists.addAll(ReadTXTUtil.readRealCode(configText.getBananaAbsolutePath(), configText.getBananaRealCodeFileName()));
        matchListsAll.addAll(ReadTXTUtil.readRealCode(configText.getBananaAbsolutePath(), configText.getBananaMatchFileName()));
        Integer[] arr = new Integer[7 * configText.getBananaExportColumn()];
        Arrays.fill(arr, 0);
        LAST_LIST = new ArrayList<>(Arrays.asList(arr));
        for (int i = 1; i < configText.getBananaCodeLength() + 1; i++) {
            ORDER_LIST.add(i);
        }
    }

    @Override
    public void logOver() {

    }

    @Override
    public void initLAST_LIST(Integer column) {

    }

    /**
     * 计算最后一行数据
     *
     * @param resultList
     */
    @Override
    public void lastRowData(List<Model> resultList) {
        AtomicInteger stepAge = new AtomicInteger(3);
        AtomicInteger stepMax = new AtomicInteger(4);
        resultList.stream().forEach(model -> {
            Integer stepA = LAST_LIST.remove(stepAge.get());
            LAST_LIST.add(stepAge.get(), Math.max(stepA, model.getAge()));
            if (model.getPrized()) {
                Integer stepM = LAST_LIST.remove(stepMax.get());
                LAST_LIST.add(stepMax.get(), stepM + 1);
            }
            stepAge.addAndGet(7);
            stepMax.addAndGet(7);
        });
    }

    @Override
    public void exportLastRowResult() {

    }

    @Override
    public void exportRowResult(Map<String, Object> params) {

    }

    @Override
    public void doPrivateJob(Map<String, Object> params, List<List<Integer>> realCodeLists) {

    }

    /**
     * 转输出data
     *
     * @param resultList
     * @return
     */
    private static Map<String, List<String>> countResult(List<Model> resultList) {
        MyTypeWriteHandler.RED_LIST.clear();
        Map<String, List<String>> map = new HashMap<>();
        List<String> ageList = new ArrayList<>();
        List<String> typeList = new ArrayList<>();
        ageList.add(1 + resultList.get(0).getRow() + "");
        typeList.add(1 + resultList.get(0).getRow() + "");
        AtomicInteger count = new AtomicInteger(0);
        AtomicInteger redIndex = new AtomicInteger(1);
        resultList.stream().forEach(model -> {
            ageList.add("" + (1 + model.getColumn()));
            ageList.add("" + (1 + model.getType()));
            typeList.add("" + (1 + model.getType()));
            ageList.add("" + model.getAge());
            ageList.add("" + model.getMatchCount());
            ageList.add("" + model.getMatchCountAll());
            ageList.add("" + model.getAveMatchCount());
            ageList.add("" + model.getPrized());
            if (model.getPrized()) {
                count.getAndIncrement();
                MyTypeWriteHandler.RED_LIST.add(redIndex.get());
            }
            redIndex.getAndIncrement();
        });
        ageList.add(count.get() == 0 ? "" : "" + count.get());
        typeList.add(count.get() == 0 ? "" : "" + count.get());
        map.put("age", ageList);
        map.put("type", typeList);
        return map;
    }

}
